package models

import (
	"bytes"
	"database/sql/driver"
	"encoding/json"
	"fmt"
	"go.didapinche.com/foundation/plat-echo/pkg/utils/data"
	"go.didapinche.com/time"
	"strconv"
	"strings"
	temp "text/template"
)

const (
	TemplateTableName = "t_template"
)

type Queries []*QueryExpression

type Template struct {
	ID            uint64         `gorm:"primary_key,column:id" json:"id"`
	Name          string         `gorm:"column:name" json:"name"`
	TypeID        uint64         `gorm:"column:type_id" json:"type_id"` //模版分类ID
	Kind          GraphKind      `gorm:"column:kind" json:"kind"`       //种类
	Sla           bool           `gorm:"sla" json:"sla"`
	Order         int            `gorm:"column:order" json:"order"`
	General       *General       `gorm:"type:json;column:general" json:"general"`
	Queries       Queries        `gorm:"type:json;column:queries" json:"queries"`
	Visualization *Visualization `gorm:"type:json;column:visualization" json:"visualization"` // 可视化
	Alert         *Alert         `gorm:"type:json;column:alert" json:"alert,omitempty"`
	SubscribeInfo *SubscribeInfo `gorm:"type:json;column:subscribe_info" json:"subscribe_info"`
	CreateTime    time.Time      `gorm:"<-:create;column:create_time" json:"create_time"`
	UpdateTime    time.Time      `gorm:"column:update_time" json:"update_time"`
	//非sql语句
	//Keys     []uint64 `gorm:"-" json:"keys"` //appId,apiId,hostId
	TypeName string `gorm:"-" json:"type_name"`
	Type     int    `gorm:"-"  json:"type,omitempty"` //覆盖级别，0：覆盖全部，2未更新，1已更新
	AppID    uint64 `gorm:"-" json:"app_id"`
	ApiID    uint64 `gorm:"-" json:"api_id"`
	HostID   uint64 `gorm:"-" json:"host_id"`
	OldVlaue string `gorm:"-" json:"old_vlaue"`
}

func (Template) TableName() string {
	return TemplateTableName
}

//深拷贝
func (t *Template) Copy() *Template {
	aj, _ := json.Marshal(t)
	b := new(Template)
	_ = json.Unmarshal(aj, b)

	fmt.Printf("%+v", b)
	return b
}

//声明一个Hero结构体切片类型
type TemplateSlice []*Template

//切片实现Interface 接口的三个方法
//1.Len() ：返回切片的大小
func (m TemplateSlice) Len() int {
	return len(m)
}

//2.Less(i, j int) :决定使用什么规则进行排序
func (m TemplateSlice) Less(i, j int) bool {
	return m[i].Order < m[j].Order
}

//3.Swap(i, j int) :Less(i, j int)返回true时进行交换
func (m TemplateSlice) Swap(i, j int) {
	m[i], m[j] = m[j], m[i]
}

type VisualizationType string
type Transform string

const (
	GraphType VisualizationType = "graph"
	Table     VisualizationType = "table"
	PieChart  VisualizationType = "pie_chart"
	Text      VisualizationType = "text"
	AlertList VisualizationType = "alertlist"
	GaugeType VisualizationType = "gauge"
)

const (
	Rows         Transform = "rows"
	Columns      Transform = "columns"
	Aggregations Transform = "aggregations"
)

type GraphKind int

type UnitType string

const (
	AppTemplate    GraphKind = iota //应用
	ApiTemplate                     //接口
	HostTemplate                    //主机
	DashboardGraph                  //仪表盘
)

const (
	Short   UnitType = "short"
	Percent UnitType = "percent"

	Ms UnitType = "ms"
	S  UnitType = "s"
	M  UnitType = "m"
	H  UnitType = "h"
	D  UnitType = "d"

	Bits      UnitType = "bits"
	Bytes     UnitType = "bytes"
	Kilobytes UnitType = "kilobytes"
	Mebibytes UnitType = "mebibytes"
	Gigabytes UnitType = "gigabytes"

	Ops    UnitType = "ops/sec(ops)"
	Reqps  UnitType = "request/sec(rps)"
	Readps UnitType = "reads/sec(rps)"
	Was    UnitType = "writes/sec(was)"
	Iops   UnitType = "I/O ops/sec(iops)"
	Opm    UnitType = "ops/min(opm)"
	Rpm    UnitType = "reads/min(rpm)"
	Wpm    UnitType = "writes /min(wpm)"
)

var Units = map[UnitType]string{
	Short:     "",
	Percent:   "%",
	Ms:        "ms",
	S:         "s",
	M:         "m",
	H:         "h",
	D:         "d",
	Bits:      "bit",
	Bytes:     "B",
	Kilobytes: "KB",
	Mebibytes: "MB",
	Gigabytes: "GB",
	Ops:       "",
	Reqps:     "",
	Readps:    "",
	Was:       "",
	Iops:      "",
	Opm:       "",
	Rpm:       "",
	Wpm:       "",
}

type SubscribeInfo struct {
	SubscribeType []SubscribeType `form:"subscribe_type" json:"subscribe_type,omitempty"`
	Owner         bool            `form:"owner" json:"owner,omitempty"`
	Contain       bool            `json:"contain"`
	Level         []int           `json:"level"`
}

type Alert struct {
	Threshold  string    `json:"threshold" yaml:"threshold"`         // 阈值
	Expression string    `json:"expression" yaml:"expression"`       // 表达式
	Message    string    `json:"message" yaml:"message"`             // 报警信息
	For        int       `json:"for" yaml:"for"`                     // 持续时长
	Receiver   *Receiver `json:"receiver,omitempty" yaml:"receiver"` // 接收群/人
	AlertLevel int       `json:"alert_level"`                        //告警级别0：警告 1：严重 2忽略
}

func (a *Alert) GetOwner() string {
	if a.Receiver == nil || len(a.Receiver.Users) == 0 {
		return ""
	}
	Owners := make([]*ApplicationOwner, 0)
	for _, a := range a.Receiver.Users {
		owner := new(ApplicationOwner)
		owner.UserID = a.ID
		owner.UserName = a.Name
		Owners = append(Owners, owner)
	}
	s, _ := json.Marshal(Owners)
	return string(s)
}

type QueryExpression struct {
	Expression string `json:"expression" yaml:"expression"`
	Legend     string `json:"legend" yaml:"legend"`
	Status     int    `json:"status" yaml:"status"` //保留字段
	Link       string `json:"link" yaml:"link"`
}

type Silence struct {
	Duration  string    `json:"duration" yaml:"duration"`     //间隔
	StartTime time.Time `json:"start_time" yaml:"start_time"` //开始时间
	EndTime   time.Time `json:"end_time" yaml:"end_time"`     //结束时间
}

type Subscription struct {
	Sms   bool `json:"sms"`
	Phone bool `json:"phone"`
}

type Unit struct {
	Base float64
	Type UnitType
}

var unitUp = make(map[UnitType]*Unit, 32)

var unitDown = make(map[UnitType]*Unit, 32)

func init() {
	unitUp[Bits] = &Unit{
		Base: 8,
		Type: Bytes,
	}
	unitUp[Bytes] = &Unit{
		Base: 1024,
		Type: Kilobytes,
	}
	unitUp[Kilobytes] = &Unit{
		Base: 1024,
		Type: Mebibytes,
	}
	unitUp[Mebibytes] = &Unit{
		Base: 1024,
		Type: Gigabytes,
	}

	unitUp[Ms] = &Unit{
		Base: 1000,
		Type: S,
	}

	unitUp[S] = &Unit{
		Base: 60,
		Type: M,
	}

	unitUp[M] = &Unit{
		Base: 60,
		Type: H,
	}

	unitUp[H] = &Unit{
		Base: 24,
		Type: D,
	}

	unitDown[D] = &Unit{
		Base: 24,
		Type: H,
	}
	unitDown[H] = &Unit{
		Base: 60,
		Type: M,
	}
	unitDown[M] = &Unit{
		Base: 60,
		Type: S,
	}
	unitDown[S] = &Unit{
		Base: 1000,
		Type: Ms,
	}

	unitDown[Gigabytes] = &Unit{
		Base: 1024,
		Type: Mebibytes,
	}
	unitDown[Mebibytes] = &Unit{
		Base: 1024,
		Type: Kilobytes,
	}
	unitDown[Kilobytes] = &Unit{
		Base: 1024,
		Type: Bytes,
	}
	unitDown[Bytes] = &Unit{
		Base: 8,
		Type: Bits,
	}

}

// 阈值单位转换
func FormatValue(value string, format UnitType) string {
	threshold := data.Compute(value)
	if format == "" {
		format = Short
	}
	switch format {
	case Short:
	case Percent:
		threshold = threshold * 100
	default:
		if threshold > 1 {
			for unitUp[format] != nil && threshold > unitUp[format].Base {
				threshold = threshold / unitUp[format].Base
				format = unitUp[format].Type
			}
		} else {
			for unitDown[format] != nil && threshold*unitDown[format].Base > 1 {
				threshold = threshold * unitDown[format].Base
				format = unitDown[format].Type
			}
		}
	}
	v, err := strconv.ParseFloat(fmt.Sprintf("%.3f", threshold), 64)
	if err != nil {
		return strconv.FormatFloat(threshold, 'f', -1, 64) + Units[format]
	}
	return strconv.FormatFloat(v, 'f', -1, 64) + Units[format]
}

type User struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

type DingDingInfo struct {
	Name  string `json:"name"`
	Token string `json:"token"`
}

type Receiver struct {
	Users     []*User         `json:"users"`
	Dingdings []*DingDingInfo `json:"dingdings"`
}

type Visualization struct {
	Type        VisualizationType `json:"type" yaml:"type"`
	Options     GraphOption       `json:"options" yaml:"options"`
	Values      []string          `json:"values" yaml:"values"`
	Text        string            `json:"text" yaml:"text"`
	Table       GraphTable        `json:"table"`
	Position    GraphPosition     `json:"position"`
	StateFilter StateFilter       `json:"state_filter"`
	Gauge       Gauge             `json:"gauge"`
}

type Gauge struct {
	Title        string       `json:"title"`
	Calc         string       `json:"calc"`
	DefaultColor string       `json:"default_color"`
	Max          float64      `json:"max"`
	Min          float64      `json:"min"`
	Thresholds   []*Threshold `json:"thresholds"`
}

type Threshold struct {
	Color string `json:"color"`
	Value int    `json:"value"`
}

type GraphTable struct {
	Paging TablePaging `json:"paging"`
	Data   TableData   `json:"data"`
}

type GraphPosition struct {
	X    int `json:"x"`
	Y    int `json:"y"`
	Rows int `json:"rows"`
	Cols int `json:"cols"`
}

type GraphOption struct {
	ShowLegend bool `json:"show_legend" yaml:"show_legend"`
	ToTheRight bool `json:"to_the_right" yaml:"to_the_right"`
	AsTable    bool `json:"as_table" yaml:"as_table"`
	Width      int  `json:"width"`
	MaxItems   int  `json:"max_items"`
}

type StateFilter struct {
	Ok       bool `json:"ok"`
	Alerting bool `json:"alerting"`
	Pending  bool `json:"pending"`
}

type TableData struct {
	Transform Transform `json:"transform"`
	Columns   []string  `json:"columns"`
}

type TablePaging struct {
	Rows   int  `json:"rows"`
	Scroll bool `json:"scroll"`
}

type GraphCount struct {
	TemplateID uint64 `json:"template_id"`
	AppID      uint64 `json:"app_id"`
	Count      int    `json:"count"`
}

func (alert *Alert) ValidateAlert() bool {
	if alert == nil {
		return false
	}
	if len(alert.Expression) == 0 || strings.Trim(alert.Expression, " ") == "" {
		return false
	}
	return true
}

func FormatLegend(legend string, data interface{}) (string, error) {
	legendTemp, _ := temp.New("legend").Parse(legend)
	buf := &bytes.Buffer{}
	err := legendTemp.Execute(buf, data)
	if err != nil {
		return "", err
	}
	return buf.String(), nil
}
func FormatLink(link string, data interface{}) (string, error) {
	if link == "" {
		return "", nil
	}
	legendTemp, _ := temp.New("link").Parse(link)
	buf := &bytes.Buffer{}
	err := legendTemp.Execute(buf, data)
	if err != nil {
		return "", err
	}
	return buf.String(), nil
}

//gorm json解析反解析
func (s *SubscribeInfo) Value() (driver.Value, error) {
	return json.Marshal(s)
}

func (s *SubscribeInfo) Scan(input interface{}) error {
	return json.Unmarshal(input.([]byte), s)
}

func (a *Alert) Value() (driver.Value, error) {
	return json.Marshal(a)
}

func (a *Alert) Scan(input interface{}) error {
	return json.Unmarshal(input.([]byte), a)
}

func (v *Visualization) Value() (driver.Value, error) {
	return json.Marshal(v)
}

func (v *Visualization) Scan(input interface{}) error {
	return json.Unmarshal(input.([]byte), v)
}

func (q Queries) Value() (driver.Value, error) {
	return json.Marshal(q)
}

func (q *Queries) Scan(input interface{}) error {
	return json.Unmarshal(input.([]byte), &q)
}

func (s *Silence) Value() (driver.Value, error) {
	return json.Marshal(s)
}

func (s *Silence) Scan(input interface{}) error {
	return json.Unmarshal(input.([]byte), s)
}

func (s *Subscribe) Value() (driver.Value, error) {
	return json.Marshal(s)
}

func (s *Subscribe) Scan(input interface{}) error {
	return json.Unmarshal(input.([]byte), s)
}

func (g *General) Value() (driver.Value, error) {
	return json.Marshal(g)
}

func (g *General) Scan(input interface{}) error {
	return json.Unmarshal(input.([]byte), g)
}
